#include <cassert>
#include <sstream>
#include <fstream>
#include <iostream>

#include <windows.h>
#include <shlobj.h>
#include <rpc.h>

#include "path.h"
#include "string.h"
#include "system.h"

// "x\jim\bob.foo" -> "bob"
std::string bare_name(const std::string &path)
{
    int dot = path.find_last_of(".");
    if(dot == std::string::npos)
        dot = path.size();

    int slash = path.find_last_of("\\/");
    if(slash == std::string::npos)
        slash = -1;

    return path.substr(slash+1, dot-slash-1);
}

// "x\jim\bob.foo" -> "x/jim"
std::string dir_name(const std::string &path)
{
    int slash = path.find_last_of("\\/");
    if(slash == std::string::npos)
        slash = 0;

    return path.substr(0, slash);
}

// "x", "x\jim\bob.foo" -> "jim\bob.foo"
std::string relative_path(std::string base, const std::string &path)
{
    if(!ends_with(base, "\\"))
        base.append("\\");

    assert(starts_with(path, base));
    return path.substr(base.size());
}

// "x/jim/bob.foo" -> "x\jim\bob.foo"
void fix_path(std::string &path)
{
    for(int i=0; i<path.size(); i++)
        if(path[i] == '/')
            path[i] = '\\';
}

bool file_exists(const std::string &path)
{
    return GetFileAttributes(path.c_str()) != 0xffffffff;
}

void find_files(const std::string &path, Strings &results)
{
    std::string glob = path + "\\*";

    WIN32_FIND_DATA data;
    SetLastError(0);
    HANDLE search = FindFirstFile(glob.c_str(), &data);
    for(;;)
    {
        DWORD err = GetLastError();
        if(err == ERROR_FILE_NOT_FOUND)
            break;
        else if(err == ERROR_NO_MORE_FILES)
            break;
        else if(err)
            report_error_and_exit(err);

        if(
            strcmp(data.cFileName, ".") &&
            strcmp(data.cFileName, ".."))
        {
            std::string abs_path = path + "\\" + data.cFileName;

            if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                find_files(abs_path, results);
            else
                results.push_back(abs_path);
        }

        FindNextFile(search, &data);
    }
    SetLastError(0);
    FindClose(search);
}

std::string temp_path()
{
    char buf[MAX_PATH];
    GetTempPath(MAX_PATH, buf);
    std::string r(buf);

    UUID uuid;
    UuidCreateSequential(&uuid);
    sprintf(
        buf,
        "{%08X-%04X-%04X-%02X%02X%02X%02X%02X%02X%02X%02X}",
        uuid.Data1,
        uuid.Data2,
        uuid.Data3,
        uuid.Data4[0], uuid.Data4[1],
        uuid.Data4[2], uuid.Data4[3],
        uuid.Data4[4], uuid.Data4[5],
        uuid.Data4[6], uuid.Data4[7]
    );
    r.append(buf);

    return r;
}

void ensure_dir_exists(const std::string &path)
{
    SHCreateDirectoryEx(0, path.c_str(), 0);
}

std::string program_path()
{
    char buf[MAX_PATH];
    GetModuleFileName(0, buf, MAX_PATH);
    return buf;
}

void recursive_delete(const std::string &dir)
{
    SetLastError(0);

    std::string glob = dir + "\\*";

    WIN32_FIND_DATA data = {0};
    HANDLE search = FindFirstFile(glob.c_str(), &data);
    for(;;)
    {
        DWORD err = GetLastError();
        if(err == ERROR_FILE_NOT_FOUND)
            break;
        else if(err == ERROR_NO_MORE_FILES)
            break;
        else if(err)
            report_error_and_exit(err);

        if(
            strcmp(data.cFileName, ".") &&
            strcmp(data.cFileName, ".."))
        {
            std::string path = dir + "\\" + data.cFileName;

            if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                recursive_delete(path);
            else
                DeleteFile(path.c_str());
        }
        FindNextFile(search, &data);
    }
    RemoveDirectory(dir.c_str());
    SetLastError(0);
    FindClose(search);
}


